﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.IO;
using System.Reflection;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading;
using System.Xml;

namespace HWUpload
{
    class Program
    {
        
        static void Main(string[] args)
        {
            if (args.Length <= 0)
            {
                Console.WriteLine("no input file");
                return;
            }
            try
            {
                /*
                 * 将当前程序工作目录设定为可执行文件工作目录便于控制台调用
                 */
                String location = Assembly.GetEntryAssembly().Location;
                Environment.CurrentDirectory = Path.GetDirectoryName(location);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return;
            }
            String huawei_user = null,huawei_pwd = null;
            String wps_user = null,wps_pwd = null;
            if (Program.GetLoginInfo("huawei", ref huawei_user, ref huawei_pwd) == false ||
                Program.GetLoginInfo("wps", ref wps_user, ref wps_pwd) == false)
            {
                Console.WriteLine("配置文件出错");
                return;
            }
            HuaWeiUpload huawei = new HuaWeiUpload();
            WPSUpload wps = new WPSUpload();
            foreach (String file_path in args)
            {
                huawei.UploadFileEx(huawei_user, huawei_pwd, file_path);
                wps.UploadFileEx(wps_user, wps_pwd, file_path);
            }
        }
        public static bool GetLoginInfo(String label,ref String user,ref String pwd)
        {
            XmlDocument xml = new XmlDocument();
            try
            {
                xml.Load("config.xml");
                XmlNode root_node = xml.GetElementsByTagName(label)[0];
                user = root_node["user"].InnerText;
                pwd = root_node["password"].InnerText;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return false;
            }
            return true;
        }
    }
    class BaseUpload
    {
        protected HttpWebRequest request;
        protected HttpWebResponse response;
        protected String response_html;
        private String str_oldProgres=null;      //显示进度条要使用这个变量
        /// <summary>
        /// 创建一个新的HTTP连接
        /// </summary>
        /// <param name="str_uri">URI地址</param>
        /// <param name="method">HTTP方法，如果为null则method=GET</param>
        /// <param name="content_type">对应HTTP头部的Content-Type</param>
        /// <param name="cookie"></param>
        /// <returns></returns>
        protected HttpWebRequest CreateNewRequest(String str_uri, String method, String content_type, CookieContainer cookie)
        {
            Uri uri = new Uri(str_uri);
            request = (HttpWebRequest)WebRequest.Create(uri);
            request.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
            request.CookieContainer = cookie == null ? new CookieContainer() : cookie;
            if (method != null) request.Method = method;
            if (content_type != null) request.ContentType = content_type;
            return request;
        }
        /// <summary>
        /// 当CreateNewRequest后再调用该方法获取服务器的响应
        /// </summary>
        /// <returns></returns>
        protected String GetResponseHtml()
        {
            response = (HttpWebResponse)request.GetResponse();
            StreamReader reader = new StreamReader(response.GetResponseStream());
            response_html = reader.ReadToEnd();
            return response_html;
        }
        /// <summary>
        /// POST一个字符串
        /// </summary>
        /// <param name="post_str"></param>
        /// <param name="encoding">编码类型</param>
        protected void PostString(String post_str, Encoding encoding)
        {
            byte[] byte_data = encoding.GetBytes(post_str);
            request.ContentLength = byte_data.Length;
            Stream post_stream = request.GetRequestStream();
            post_stream.Write(byte_data, 0, byte_data.Length);
            post_stream.Flush();
        }
        /// <summary>
        /// 在REQUEST流中POST一个字符串,和PostString的区别是，
        /// PostString单单只是发送一个字符串
        /// 而PostDataString则是在POST数据中插入一个字符串，
        /// 因此这里没有设置ContentLength字段
        /// </summary>
        /// <param name="post_str"></param>
        /// <param name="encoding">字符串采用的编码类型</param>
        protected void PostDataString(String post_str, Encoding encoding)
        {
            byte[] byte_data = encoding.GetBytes(post_str);
            Stream post_stream = request.GetRequestStream();
            post_stream.Write(byte_data, 0, byte_data.Length);
            post_stream.Flush();
        }
        /// <summary>
        /// 简单的显示进度
        /// </summary>
        /// <param name="progress"></param>
        protected void ShowProgress(Int32 progress)
        {
            String fmt = String.Format("{0}%", progress);
            String back_str=null;
            if (str_oldProgres != null)
                back_str = str_oldProgres.Length == 2 ? "\b\b" : "\b\b\b";
            Console.Write(back_str);
            Console.Write(fmt);
            if (progress == 100) Console.WriteLine("");
            str_oldProgres = fmt;
        }
    }
    class HuaWeiUpload : BaseUpload
    {
        private bool Login(String user, String pwd,CookieContainer cookie)
        {
            this.CreateNewRequest("http://m.dbank.com/user/user.action", "POST",
                        "application/x-www-form-urlencoded", cookie);
            String post_string = String.Format("user.account={0}&user.password={1}&ru=",user,pwd);
            this.PostString(post_string, Encoding.ASCII);
            this.GetResponseHtml();
            return response_html.Contains("密码错误") ? false : true;
        }
        private bool HuaweiPostFile(String html, String file_path, CookieContainer cookie)
        {
            Regex regex = new Regex("name=\"nsp_key\" value=\"(.*?)\"", RegexOptions.Singleline | RegexOptions.Multiline);
            /*
             *华为网盘利用该值来验证用户身份，这个只是我的猜测，不然没有cookie根本无法验证身份
             */
            String nsp_key = regex.Match(html).Groups[1].ToString();
            regex = new Regex("name=\"nsp_ts\" value=\"(.*?)\"", RegexOptions.Singleline | RegexOptions.Multiline);
            /*
             *这个是本次提交的时间，也是网页返回的
             */
            String time_str = regex.Match(html).Groups[1].ToString();
            String bounary_str = "--" + "ZeroLYForEver";
            String head_str = bounary_str + "\r\n" +
                              "Content-Disposition: form-data; name=\"files\"; filename=\"" + Path.GetFileName(file_path) +
                                "\"\r\nContent-Type: application/octet-stream\r\n\r\n";
            String nail_str = "\r\n" + bounary_str + "\r\n" + "Content-Disposition: form-data; name=\"nsp_fmt\"\r\n\r\n" + "HEADER_LOCATION\r\n" +
                            bounary_str + "\r\n" + "Content-Disposition: form-data; name=\"nsp_cb\"\r\n\r\n" + "http://m.dbank.com/netdisk/upload.action?dcont=" + "\r\n" +
                            bounary_str + "\r\n" + "Content-Disposition: form-data; name=\"nsp_app\"\r\n\r\n1\r\n" +
                            bounary_str + "\r\n" + "Content-Disposition: form-data; name=\"nsp_retname\"\r\n\r\n0\r\n" +
                            bounary_str + "\r\n" + "Content-Disposition: form-data; name=\"nsp_ts\"\r\n\r\n" + time_str + "\r\n" +
                            bounary_str + "\r\n" + "Content-Disposition: form-data; name=\"nsp_key\"\r\n\r\n" + nsp_key + "\r\n" +
                            bounary_str + "\r\n" + "Content-Disposition: form-data; name=\"submit\"\r\n\r\n上传\r\n" + bounary_str + "--\r\n";
            /*
             * 这个是分隔符，也是本次我最花时间的地方，查了RFC1867文档才知道
             * 在HTTP头部声明的boundary和POST数据中的boundary是有两个字符("--")
             * 的差异，而且要在最后一个分隔符多加两个"--"，因为浏览器会自动生成很多'-'
             * 所以是很难发现的-_-
            */
            String content_type = "multipart/form-data; boundary=" + bounary_str.Remove(0, 2);
            this.CreateNewRequest("http://upload.dbank.com/upload/up.php", "POST", content_type, cookie);
            request.Referer = "http://m.dbank.com/xhtml/netdisk/upload.jsp";
            byte[] byte_file = new byte[4096];
            FileStream file_stream=null;
            try
            {
                file_stream = new FileStream(file_path, FileMode.Open);
                request.ContentLength = Encoding.UTF8.GetBytes(head_str).Length + 
                                        Encoding.UTF8.GetBytes(nail_str).Length + 
                                        file_stream.Length;
                this.PostDataString(head_str, Encoding.UTF8);   //发送头部
                long file_len = file_stream.Length;
                long have_read = 0;
                Stream request_stream=request.GetRequestStream();
                Console.Write("\t正在上传文件\""+Path.GetFileName(file_path)+"\"到华为网盘......");
                while (have_read < file_len)
                {
                    Int32 ret = file_stream.Read(byte_file, 0, 4096);
                    request_stream.Write(byte_file, 0, ret);
                    have_read += ret;
                   this.ShowProgress((Int32)(have_read*100 / file_len));
                }
                file_stream.Close();
                this.PostDataString(nail_str, Encoding.UTF8);   //发送尾部
                this.response_html = this.GetResponseHtml();
                /*
                 * 向用户反馈信息
                 */
                regex = new Regex("<div>(.*?)</div>", RegexOptions.Multiline | RegexOptions.Singleline);
                MatchCollection mc = regex.Matches(response_html);
                foreach (Match m in mc)
                {
                    Console.Write("\t\t");
                    Console.WriteLine(m.Groups[1].ToString());
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                if (file_stream != null)
                    file_stream.Close();
                return false;
            }
            return true;
        }
        public void UploadFileEx(String user, String pwd, String file)
        {
            //登录首页获取cookie才能进行接下来的操作，这步不可省略
            this.CreateNewRequest("http://m.dbank.com",null,null,null);
            this.GetResponseHtml();
            CookieContainer cookies = request.CookieContainer;

            //登录到账户
            bool is_login = this.Login(user, pwd,cookies);
            if (is_login == false)
            {
                Console.WriteLine("登录失败");
                return;
            }
            cookies = request.CookieContainer;

            //进入上传页面获取上传参数
            this.CreateNewRequest("http://m.dbank.com/xhtml/netdisk/upload.jsp", null, null, cookies);
            this.GetResponseHtml();
            //上传文件
            bool is_ok=this.HuaweiPostFile(this.response_html, file, cookies);

            //退出登录
            this.CreateNewRequest("http://m.dbank.com/user/logout.action", null, null, cookies);
            this.GetResponseHtml();
        }
  
    }
    class WPSUpload : BaseUpload
    {
        private bool Login(String user, String pwd, CookieContainer cookie)
        {
            this.CreateNewRequest("https://www.kuaipan.cn/index.php?ac=account&op=login", "POST",
                        "application/x-www-form-urlencoded; charset=UTF-8", cookie);
            String post_str = String.Format("username={0}&userpwd={1}&isajax=yes", user, pwd);
            this.PostString(post_str, Encoding.ASCII);
            this.GetResponseHtml();
            if (this.response_html.Contains("\"state\":\"1\"")) return true;
            return false;
        }
        private bool WPSPostFile(String html, String file_path, CookieContainer cookie)
        {
            String file_name=Path.GetFileName(file_path);
            Regex rx = new Regex("token\":\"(.*?)\"", RegexOptions.Singleline | RegexOptions.Multiline);
            String token = rx.Match(html).Groups[1].ToString();
            String uri_str = "http://p5.dfs.kuaipan.cn:8080/cdlnode/ul/?parentId=0&name=" + file_name;
            String bd_str="-----------"+DateTime.Now.Ticks.ToString();
            String post_head=   bd_str+"\r\nContent-Disposition: form-data; name=\"fileData\"; filename=\""+
                                file_name+"\"\r\nContent-Type: application/octet-stream\r\n\r\n";
            String post_nail=   "\r\n"+bd_str+"\r\nContent-Disposition: form-data; name=\"token\"\r\n\r\n" + 
                                token + "\r\n" + bd_str + "--\r\n";
            String content_type="multipart/form-data; boundary=" + bd_str.Remove(0,2);
            this.CreateNewRequest(uri_str, "POST", content_type, cookie);
            byte[] byte_file = new byte[4096];  //4KB
            Console.Write("\t正在上传文件\"" + file_name + "\"到金山快盘......");
            FileStream file_stream = null;
            try
            {
                file_stream = new FileStream(file_path, FileMode.Open);
                request.ContentLength = Encoding.UTF8.GetBytes(post_head).Length +
                                        Encoding.UTF8.GetBytes(post_nail).Length +
                                        file_stream.Length;
                this.PostDataString(post_head, Encoding.UTF8);      //发送头部
                long file_len = file_stream.Length;
                long have_read = 0;
                Stream request_stream = request.GetRequestStream();
                while (have_read < file_len)
                {
                    Int32 ret = file_stream.Read(byte_file, 0, 4096);
                    request_stream.Write(byte_file, 0, ret);
                    have_read += ret;
                    this.ShowProgress((Int32)(have_read * 100 / file_len));
                }
                file_stream.Close();
                this.PostDataString(post_nail, Encoding.UTF8);   //发送尾部
                this.response_html = this.GetResponseHtml();
                Console.WriteLine("\t\t上传完成!!!");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                if (file_stream != null)
                    file_stream.Close();
                return false;
            }
            return true;
        }
        public void UploadFileEx(String user, String pwd, String file_path)
        {
            this.CreateNewRequest("https://www.kuaipan.cn/account_login.htm",null,null,null);
            this.GetResponseHtml();
            CookieContainer cookies = this.request.CookieContainer;

            //登录
            bool ret=this.Login(user,pwd,cookies);
            if (ret == false)
            {
                Console.WriteLine("登录失败");
                return;
            }

            cookies = this.request.CookieContainer;
            /*
             * 和华为网盘不同这里少了一步进入上传界面，因为登录的时候
             * 服务器已经利用JOSN方式将KEY传过来，什么是JOSN，这个我也
             * 不懂，知道有这个东西而已
             */
            this.WPSPostFile(response_html, file_path, cookies);

            //顺便签下到
            this.CreateNewRequest("http://www.kuaipan.cn/index.php?ac=zone&op=taskdetail",null,null,cookies);
            this.GetResponseHtml();
            //退出登录
            this.CreateNewRequest("http://www.kuaipan.cn/account_logout.htm", null, null, cookies);
            this.GetResponseHtml();
        }
    }
}
